package com.weedsdream.app.struct

import android.app.Activity
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.net.Uri
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.ui.graphics.toArgb
import androidx.core.content.FileProvider
import com.google.gson.Gson
import com.weedsdream.BaseActivity
import com.weedsdream.BaseActivity.Companion.activity
import com.weedsdream.app.colorplate.ColorSetData
import com.weedsdream.app.colorplate.ColorSetType
import com.weedsdream.app.colorplate.ColorSetType.使用时间
import com.weedsdream.app.colorplate.ColorSetType.收藏
import com.weedsdream.app.colorplate.MAX_PEN_SIZE
import com.weedsdream.app.colorplate.colorSet
import com.weedsdream.app.compose.history
import com.weedsdream.function.inCoroutine
import com.weedsdream.function.toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStreamWriter

/**文件操作*/
object FileOpe {
    /**基础目录*/
    private val dir = activity.filesDir

    /**保存颜色集:使用时间-收藏*/
    fun saveColorSet() {
        inCoroutine(Dispatchers.IO) {
            val file = File(dir, "颜色集.txt")
            FileOutputStream(file).use { fos ->
                OutputStreamWriter(fos).use { writer ->
                    writer.write("历史:\n")
                    colorSet[使用时间]?.forEach {
                        writer.write("${it.id},${it.time}\n") // 每个项后面加换行符
                    }
                    writer.write("收藏:\n")
                    colorSet[收藏]?.forEach {
                        writer.write("${it.id}\n") // 每个项后面加换行符
                    }
                }
            }
        }
    }

    /**加载颜色集*/
    fun readColorSet() {
        inCoroutine(Dispatchers.IO) {
            val file = File(dir, "颜色集.txt")
            val listTim = mutableListOf<Pair<Int, Long>>()
            val listFav = mutableListOf<Int>()
            var type: ColorSetType? = null
            if (file.exists()) {
                FileInputStream(file).use { fis ->
                    InputStreamReader(fis).use { isr ->
                        BufferedReader(isr).use { reader ->
                            reader.forEachLine { line ->
                                when {
                                    line.startsWith("历史:") -> type = 使用时间
                                    line.startsWith("收藏:") -> type = 收藏
                                    else -> when (type) {
                                        使用时间 -> {
                                            val s = line.split(",")
                                            if (s.size == 2) {
                                                val id = s[0].toIntOrNull()
                                                val tm = s[1].toLongOrNull()
                                                if (id != null && tm != null) listTim.add(
                                                    Pair(id, tm)
                                                )
                                            }
                                        }

                                        收藏 -> line.toIntOrNull()?.let { listFav.add(it) }
                                        else -> {}
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (listTim.isNotEmpty()) colorSet[使用时间]?.apply {
                clear()
                for ((cid, tm) in listTim) {
                    val csd = ColorSetData(ColorSetData.int2color(cid))
                    csd.time = tm
                    add(csd)
                }
            }
            if (listFav.isNotEmpty()) colorSet[收藏]?.apply {
                clear()
                for (cid in listFav) add(ColorSetData(ColorSetData.int2color(cid)))
            }
        }
    }

    /**读取工程目录*/
    fun readProjectMenu() {
        inCoroutine(Dispatchers.Default) {
            val files = dir.listFiles()
            if (files == null || files.isEmpty()) return@inCoroutine
            val map = mutableMapOf<String, RecordFile>()
            //加载工程
            val curProject = SpOpe.read()
            for (file in files) if (file.isFile && file.name.startsWith("project_")) {
                val name = file.name.removePrefix("project_").removeSuffix(".txt")
                if (name == curProject) continue//当前加载的文件不记录
                map[name] = RecordFile(file.lastModified(), name, file.absolutePath)
            }
            //匹配缩略图
            for (file in files) if (file.isFile && file.name.startsWith("thumbnail_")) {
                val name = file.name.removePrefix("thumbnail_").removeSuffix(".png")
                if (map[name] != null) map[name]!!.thumbnail = file.absolutePath else file.delete()
            }
            //为无缩略图文件创建缩略图
            for (rf in map.values) if (rf.thumbnail == null) {
                val srcFile = File(rf.path)
                val draftMap = file2draftsMap(srcFile)
                if (draftMap == null) {//文件内容无效，删除此文件
                    srcFile.delete()
                    continue
                }
                rf.thumbnail = saveBitmap(rf.name, draftMap)
            }
            //更新历史
            history.clear()
            delay(100)//等待UI刷新，避免数据错位
            history.addAll(map.values)
            history.sortByDescending { it.time }
        }
    }

    /**保存当前工程*/
    fun saveProject(fileName: String) {
        val draftMap = drafts2draftsMap()
        if (draftMap == null) {
            if (fileName != SpOpe.read()) toast("当前画布无数据")
        } else {
            val file = File(dir, "project_$fileName.txt")
            draftsMap2file(file, draftMap)
            inCoroutine(Dispatchers.Default) {
                saveBitmap(fileName, draftMap)
            }
        }
    }

    /**加载工程
     * @return 是否加载成功*/
    fun readProject(fileName: String): Boolean {
        val file = File(dir, "project_$fileName.txt")
        if (!file.exists()) {
            if (fileName != SpOpe.read()) toast("文件${fileName}不存在")
            return false
        }
        val draftMap = file2draftsMap(file)
        if (draftMap == null) {
            toast("文件${fileName}数据无效")
            return false
        }
        draftsMap2drafts(draftMap)
        return true
    }

    /**删除工程*/
    fun deleteProject(fileName: String) {
        val txt = File(dir, "project_$fileName.txt")
        if (txt.exists()) txt.delete()
        val png = File(dir, "thumbnail_$fileName.png")
        if (png.exists()) png.delete()
    }

    /**自动保存当前工程*/
    fun saveAutoProject() {
        var lsName = SpOpe.read()
        if (lsName == null) {
            lsName = RecordFile().name
            SpOpe.save(lsName)
        }
        saveProject(lsName)
    }

    /**加载自动保存的工程*/
    fun readAutoProject() {
        val lsName = SpOpe.read()
        if (lsName != null) {//有保存文件则读取，否则使用默认的空画板
            if (!readProject(lsName)) {
                SpOpe.delete()
                deleteProject(lsName)
            }
        }
    }

    /**导入文件*/
    fun fromFile() {
        imFile.launch("text/plain")
    }

    /**导出文件*/
    fun toFile() {
        val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "text/plain"
            putExtra(
                Intent.EXTRA_TITLE, "${activity.getAppName()}${RecordFile().name}导出.txt"
            )
        }
        exFile.launch(intent)
    }

    /**保存图片文件*/
    fun savePNG(recordFile: RecordFile) {
        with(activity) {
            val path = recordFile.thumbnail ?: return
            val bitmap = BitmapFactory.decodeFile(path) ?: return
            val cv = ContentValues().apply {
                put(MediaStore.Images.Media.DISPLAY_NAME, recordFile.name)
                put(MediaStore.Images.Media.MIME_TYPE, "image/png")
                put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/${activity.getAppName()}")
            }
            inCoroutine(Dispatchers.IO) {
                contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cv)?.let {
                    contentResolver.openOutputStream(it)?.use { outputStream ->
                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
                        toast("文件已保存到相册:${activity.getAppName()}${recordFile.name}")
                    }
                }
            }
        }
    }

    /**分享图像*/
    fun sharePNG(recordFile: RecordFile, context: Context = activity) {
        val path = recordFile.thumbnail ?: return
        val bitmap = BitmapFactory.decodeFile(path) ?: return
        val file = File(context.cacheDir, "screenshot_${System.currentTimeMillis()}.png")
        try {
            FileOutputStream(file).use { out ->
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
                out.flush()
            }
            val uri = FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
            val shareIntent = Intent().apply {
                action = Intent.ACTION_SEND
                putExtra(Intent.EXTRA_STREAM, uri)
                type = "image/png"
                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            }
            context.startActivity(Intent.createChooser(shareIntent, "Share image"))
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    /**调用文件选择器导入文件*/
    private lateinit var imFile: ActivityResultLauncher<String>

    /**导出文件*/
    private lateinit var exFile: ActivityResultLauncher<Intent>

    /**初始化ActivityResultLauncher，需要在onCreate中调用*/
    fun initActivityResultRegistry(act: BaseActivity) {
        imFile = act.registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
            if (uri != null) try {
                var fileName = RecordFile().name
                //获取文件信息
                act.contentResolver.query(uri, null, null, null, null)?.use {
                    if (it.moveToFirst()) {
                        val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                        val name = it.getString(nameIndex)
                        fileName = name.removePrefix("project_").removeSuffix(".txt")
                    }
                }
                //读取文件内容
                val sb = StringBuilder()
                act.contentResolver.openInputStream(uri)?.use {
                    BufferedReader(InputStreamReader(it)).use { reader ->
                        var line: String?
                        while (reader.readLine().also { l -> line = l } != null) {
                            sb.append(line).append("\n")
                        }
                    }
                }
                val draftMap = string2draftMap(sb.toString())
                if (draftMap == null) {
                    toast("无法识别此文件数据")
                    return@registerForActivityResult
                }
                //保存到app目录下
                val file = File(dir, "project_${fileName}.txt")
                draftsMap2file(file, draftMap)
                inCoroutine(Dispatchers.Default) {
                    saveBitmap(fileName, draftMap)
                    //加载
                    draftsMap2drafts(draftMap)
                    readProjectMenu()
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        exFile = act.registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                val draftMap = drafts2draftsMap()
                if (draftMap == null) {
                    toast("当前画布无数据")
                    return@registerForActivityResult
                }
                result.data?.data?.let {
                    try {
                        act.contentResolver.openOutputStream(it)?.use { osm ->
                            val json = Gson().toJson(draftMap)
                            osm.write(json.toByteArray())
                            toast("文件已导出")
                        }
                    } catch (e: IOException) {
                        toast("导出失败")
                        e.printStackTrace()
                    }
                }
            }
        }
    }

    /**保存缩略图
     * @param fileName 缩略图名
     * @param draftMap 要绘图的数据
     * @return 文件路径*/
    private suspend fun saveBitmap(fileName: String, draftMap: Map<Int, List<PathMap>>): String {
        //计算画布尺寸
        var x0 = 0f//最左
        var x1 = 0f//最右
        var y0 = 0f//最上
        var y1 = 0f//最下
        for (paths in draftMap.values) for (path in paths) for (p in path.points) {
            if (x0 > p.x) x0 = p.x
            if (x1 < p.x) x1 = p.x
            if (y0 > p.y) y0 = p.y
            if (y1 < p.y) y1 = p.y
        }
        MAX_PEN_SIZE.also {//根据笔迹可用最大值在周围预留一半空间，避免边界笔迹被切割
            x0 -= it
            y0 -= it
            x1 += it
            y1 += it
        }
        val w = (x1 - x0).toInt() + 1
        val h = (y1 - y0).toInt() + 1
        val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        val paint = Paint()
        //开始绘制
        for ((colorId, paths) in draftMap) {
            paint.color = ColorSetData.int2color(colorId).toArgb()
            for (path in paths) {
                paint.strokeWidth = path.penSize
                for (i in 0 until path.points.size - 1) {
                    val s = path.points[i]
                    val e = path.points[i + 1]
                    canvas.drawLine(s.x - x0, s.y - y0, e.x - x0, e.y - y0, paint)
                }
            }
        }
        val file = File(dir, "thumbnail_$fileName.png")
        withContext(Dispatchers.IO) {
            FileOutputStream(file).use {
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
                Log.i("缩略图", file.absolutePath)
            }
        }
        bitmap.recycle()//释放资源
        return file.absolutePath
    }
}